package solverStrategies;

import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import sudoku.SudokuEinzelfeld;
import sudoku.SudokuSpielfeld;
import algo.ConvertStructToArray;

/**
 * Naked Single: Pruefe jede Zeile, Spalte und jeden Block und streiche aus den
 * Feldern die schon vorkommenden Zahlen. Bleibt nur 1 Zahl als Moeglichkeit,
 * ist dies der einzig moegliche Platz.
 * 
 * Baut auf den Rohdaten des Sudoku auf und braucht keine Vorverarbeitung
 * 
 * */
public class NakedSingle implements SolverAlgorithm {

	private final int LEVEL = 1;
	private boolean changes;

	@Override
	public boolean manipulateSudoku(SudokuSpielfeld sf) {
		Map<Integer, Set<Integer>> numbers = new TreeMap<Integer, Set<Integer>>();

		boolean localchanges = false;
		changes = false;
		do {
			localchanges = false;
			numbers = getPossibleNumbers(sf);
			Set<Integer> keys = numbers.keySet();
			List<Integer> valuesSorted = new LinkedList<Integer>(keys);
			Collections.sort(valuesSorted);

			for (Integer integer : valuesSorted) {
				Set<Integer> value = numbers.get(integer);
				if (value.size() == 1) {
					int number = (Integer) value.toArray()[0];
					sf.setNumberWithID(integer, number, "Naked Single");
					localchanges = true;
					changes = true;
				}

			}
		} while (localchanges);

		Set<Integer> keys = numbers.keySet();
		int rows = 0;
		return changes;
	}

	public final Map<Integer, Set<Integer>> getPossibleNumbers(
			SudokuSpielfeld sf) {
		Map<Integer, Set<Integer>> possibleNumbers = sf.getPossibleNumbers();
		ConvertStructToArray conv = new ConvertStructToArray(sf);
		for (int rows = 0; rows < sf.getFIELDLENGTH(); rows++) {
			for (int idx = 0; idx < sf.getFIELDLENGTH(); idx++) {
				SudokuEinzelfeld field = sf.returnEinzelnesFeld(rows, idx);
				if (field.getNumber() == 0) {
					Set<Integer> possNrs = field.getPossibleNumbers();
					/* There is only one possible number = solution */
					/* calculate the possible numbers */
					Set<Integer> newPossNrs = conv
							.returnPossibleNumbersWithLookUpToOtherStructs(field);

					/*
					 * newPossNrs containing the complete new calculated
					 * possible numbers. Example: newPossNrs = {1,2,3,4} The
					 * saved numbers for the field are {1,2,4,5} Then the
					 * solution is the intersection of the sets = {1,2,4}
					 */
					Set<Integer> savedPossNrs = field.getPossibleNumbers();

					Iterator<Integer> iter = savedPossNrs.iterator();
					while (iter.hasNext()) {
						Integer tmp = iter.next();
						if (!(savedPossNrs.contains(tmp) && newPossNrs
								.contains(tmp))) {
							iter.remove();
						}
					}

					possibleNumbers.put(field.getId(),
							field.getPossibleNumbers());

				} else {
					possibleNumbers.put(field.getId(), new HashSet<Integer>());
				}

			}
		}

		return possibleNumbers;
	}

	private void removeFromConstructs(SudokuSpielfeld sf, SudokuEinzelfeld sef) {
		List<SudokuEinzelfeld> row = sf.returnRow(sef.getRow());
		List<SudokuEinzelfeld> block = sf.returnblock(sef.getBlockNr());
		List<SudokuEinzelfeld> col = sf.returnCol(sef.getCol());
		for (int i = 0; i < row.size(); i++) {
			SudokuEinzelfeld sefTmp = row.get(i);
			sefTmp.removeNumberFromPossibleNumbers(sef.getNumber());
			sefTmp = col.get(i);
			sefTmp.removeNumberFromPossibleNumbers(sef.getNumber());
			sefTmp = block.get(i);
			sefTmp.removeNumberFromPossibleNumbers(sef.getNumber());
		}

	}

	@Override
	public int returnLevel() {
		return LEVEL;
	}

}
